#include "Highrise.hpp"

Highrise::Highrise()
:   m_shader( NULL ),
	numFaces( 0 ),
	m_sides( NULL )
{
	m_shader = ShaderLibrary::get( "defaultShader" );
    m_sides = getTexture( ":/textures/onefloor.jpg" );
	
	GLenum errCode;
	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
		string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Highrise", "Highrise()", errString.c_str(), Logger::Error );
	}
}

void Highrise::draw()
{
	if( m_shader )
		m_shader->bind();
	
	if( m_sides )
		m_sides->bind();
	
	glPushMatrix();
	glTranslatef( m_x, 0, m_z );

	drawShadow();
	
	
	glPopMatrix();
	
	if( m_shader )
		m_shader->unbind();
	
	if( m_sides )
		m_sides->unbind();
}

void Highrise::drawShadow()
{
	assert( glGetError() == GL_NO_ERROR );
	
	glPushMatrix();
	glTranslatef( m_x, 0, m_z );

	glNormalPointer( GL_FLOAT, 0, m_normals );
	glTexCoordPointer( 2, GL_FLOAT, 0, m_uvs );
	glVertexPointer( 3, GL_FLOAT, 0, m_verts );
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY );
	glEnableClientState( GL_TEXTURE_COORD_ARRAY );

	assert( glGetError() == GL_NO_ERROR );
	
	
	glDrawArrays(GL_QUADS, 0, 4*numFaces );
	assert( glGetError() == GL_NO_ERROR );
	
	glPopMatrix();
		
}

void Highrise::drawSimple()
{
	if( m_sides )
		m_sides->bind();
	
	if( m_shader )
		m_shader->bind();
	
	
	drawShadow();
	
	
	if( m_sides )
		m_sides->unbind();
	
	if( m_shader )
		m_shader->unbind();	
}


void Highrise::setGenerationRectangle( float x1, float x2, float y1, float y2 )
{
	m_maxWidth = x2 - x1;
	m_maxDepth = y2 - y1;
	m_nominalPositionX = x1 + m_maxWidth/2.0f;
	m_nominalPositionY = y1 + m_maxDepth/2.0f;
}

void Highrise::generate( Random &r )
{
	GLenum errCode;
	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
		string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Highrise", "generate( Random& ) @ 1", errString.c_str(), Logger::Error );
	}
	
	
	m_width = r.doubleNext( m_maxWidth/2.0f, m_maxWidth );
	m_depth = r.doubleNext( m_maxDepth/2.0f, m_maxDepth );
	
	m_x = m_nominalPositionX + r.doubleNext( -(m_maxWidth-m_width), (m_maxWidth-m_width) );
	m_z = m_nominalPositionY + r.doubleNext( -(m_maxDepth-m_depth), (m_maxDepth-m_depth) );

	
	m_floors = r.next( 8, 20 );
	m_floorHeight = r.doubleNext( 3.0, 5.0f );

	m_list = glGenLists(1);

	errCode = glGetError();
	if (errCode != GL_NO_ERROR)
	{
		string errString = (const char*)gluErrorString(errCode);
        Logger::write( "Highrise", "generate( Random& ) @ 2", errString.c_str(), Logger::Error );
	}
	
	if( m_list == 0 )
        Logger::write( "Highrise", "generate( Random& ) @ 2", "No list", Logger::Error );

	
	m_depth /= 2.0f;
	m_width /= 2.0f;
	
	glColor4f( 0, 1, 0, 1 );
	
	
//	HighriseType type = (HighriseType)( r.next(0, NUMBER_OF_TYPES-1 ) );
//	
//	switch( type )
//	{
//		case LEDGES:
			createLedges( r );
//			break;
//		case ARC:
//			createArc( r );
//			break;
//		default:
//			throw "bad highrise type";
//	}

	createArrays();
}


void Highrise::createArc( Random &r )
{
	int next = 10;
	for( int arc=0; arc<360; arc += next )
	{
		// Step around the building by a random amount;
		next = r.next( 10, 120 );
		
		// But don't step too far
		if( arc + next > 360 )
			next = 360 - arc;

	//	glColor3f( r.doubleNext(1), r.doubleNext(1), r.doubleNext(1) );
		
		const float rads = arc * 3.14159 / 180.0f;
		const float nextRads = next * 3.14159 / 180.0f;
		
		const float cosAngle = cos( rads );
		const float cosAngleNext = cos( rads + nextRads );
		const float sinAngle = sin( rads );
		const float sinAngleNext = sin( rads + nextRads );
		
		// Generate the hull at each floor
		for( int i=1; i<=m_floors; ++i )
		{		
//			float mfloorHeight = float(i)*m_floorHeight;
//			float pfloorHeight = float(i-1)*m_floorHeight;

			
//			
//			m_quads.push_back( sinAngle, 0, cosAngle,
//							  m_width*sinAngle, pfloorHeight, m_depth*cosAngle, 0, 0,
//							  sinAngleNext, 0, cosAngleNext,
//							  m_width*sinAngleNext, pfloorHeight, m_depth*cosAngleNext, 1, 0,
//							  sinAngleNext, 0, cosAngleNext,
//							  m_width*sinAngleNext, mfloorHeight, m_depth*cosAngleNext, 1, 1,
//							  sinAngle, 0, cosAngle,
//							  m_width*sinAngle, mfloorHeight, m_depth*cosAngle, 0, 1 );
		}
		
		
		
		
		// Top face
		float tfloorHeight = m_floors*m_floorHeight;
		
		m_quads.push_back( Quad( 0, 1, 0,
						   0, tfloorHeight, 0, 0, 0,
						   0, tfloorHeight, 0, 0, 0,
						   m_width*sinAngle, tfloorHeight,  m_depth*cosAngle, 0, 0,
						   m_width*sinAngleNext, tfloorHeight,  m_depth*cosAngleNext, 0, 0  ) );
	}	
}



void Highrise::createLedges( Random& )
{
	for( int i=1; i<m_floors; ++i )
	{
		float depth = m_depth;
		float width = m_width;
		float ledgeDepth = 0.75f;
		float ledgeWidth = 0.75f;
		
		float nfloorHeight = float(i)*m_floorHeight;

		float mfloorHeight = (float(i)-0.25f)*m_floorHeight;
		float pfloorHeight = float(i-1)*m_floorHeight;
	
		
		m_quads.push_back( Quad( 0, 0, 1,
						   -width, pfloorHeight, depth, 0, 0,
						    width, pfloorHeight, depth, 1, 0,
						    width, mfloorHeight, depth, 1, 1,
						   -width, mfloorHeight, depth, 0, 1 ) );
		m_quads.push_back( Quad( 0, 0, -1,
						   -width, mfloorHeight, -depth, 0, 1,
						    width, mfloorHeight, -depth, 1, 1,
						    width, pfloorHeight, -depth, 1, 0,
						   -width, pfloorHeight, -depth, 0, 0 ) );

		m_quads.push_back( Quad( 1, 0, 0,
						   width, mfloorHeight,  depth, 1, 1,
						   width, pfloorHeight,  depth, 1, 0,
						   width, pfloorHeight, -depth, 0, 0,		
						   width, mfloorHeight, -depth, 0, 1 ) );
		
		m_quads.push_back( Quad( -1, 0, 0,
						   -width, mfloorHeight, -depth, 0, 1,
						   -width, pfloorHeight, -depth, 0, 0,
						   -width, pfloorHeight,  depth, 1, 0,
						   -width, mfloorHeight,  depth, 1, 1 ) );

// --- ledges
		m_quads.push_back( Quad( 0, -1, 0,
						  -width-ledgeWidth,  mfloorHeight,  depth+ledgeDepth, 0, 0,
						  -width-ledgeWidth,  mfloorHeight, -depth-ledgeDepth, 0, 0,
						   width+ledgeWidth,  mfloorHeight, -depth-ledgeDepth, 0, 0,
						   width+ledgeWidth,  mfloorHeight,  depth+ledgeDepth, 0, 0 ) );
		m_quads.push_back( Quad( 0, 0, 1,
						  -width-ledgeWidth, mfloorHeight,  depth+ledgeDepth, 0, 0,
						   width+ledgeWidth, mfloorHeight,  depth+ledgeDepth, 0, 0,
						   width+ledgeWidth, nfloorHeight,  depth+ledgeDepth, 0, 0,
						  -width-ledgeWidth, nfloorHeight,  depth+ledgeDepth, 0, 0 ) );
		
		m_quads.push_back( Quad( 0, 0, -1,
						  -width-ledgeWidth, nfloorHeight, -depth-ledgeDepth, 0, 0,
						   width+ledgeWidth, nfloorHeight, -depth-ledgeDepth, 0, 0,
						   width+ledgeWidth, mfloorHeight, -depth-ledgeDepth, 0, 0,
						  -width-ledgeWidth, mfloorHeight, -depth-ledgeDepth, 0, 0 ) );

		m_quads.push_back( Quad( 1, 0, 0,
						  width+ledgeWidth, nfloorHeight,  depth+ledgeDepth, 0, 0,
						  width+ledgeWidth, mfloorHeight,  depth+ledgeDepth, 0, 0,
						  width+ledgeWidth, mfloorHeight, -depth-ledgeDepth, 0, 0,
						  width+ledgeWidth, nfloorHeight, -depth-ledgeDepth, 0, 0 ) );

		m_quads.push_back( Quad( -1, 0, 0,
						  -width-ledgeWidth, nfloorHeight, -depth-ledgeDepth, 0, 0,
						  -width-ledgeWidth, mfloorHeight, -depth-ledgeDepth, 0, 0,
						  -width-ledgeWidth, mfloorHeight,  depth+ledgeDepth, 0, 0,
						  -width-ledgeWidth, nfloorHeight,  depth+ledgeDepth, 0, 0 ) );

		m_quads.push_back( Quad( 0, 1, 0,
						  width+ledgeWidth, nfloorHeight, -depth-ledgeDepth, 0, 0,
						 -width-ledgeWidth, nfloorHeight, -depth-ledgeDepth, 0, 0,
						 -width-ledgeWidth, nfloorHeight,  depth+ledgeDepth, 0, 0,
						  width-ledgeWidth, nfloorHeight,  depth+ledgeDepth, 0, 0 ) );
	}

}



void Highrise::createArrays()
{
	numFaces = m_quads.size();
	m_normals = new float[ numFaces*4*3 ];
	m_verts = new float[ numFaces*4*3 ];
	m_uvs = new float[ numFaces*4*2 ];
	
	int index = 0;
	for( int i=0; i<numFaces; ++i )
	{
		Quad q = m_quads[ i ];
		
		for( int v=0; v<4; ++v )
		{
			m_normals[ 3*index ] = q.nx;
			m_normals[ 3*index+1 ] = q.ny;
			m_normals[ 3*index+2 ] = q.nz;
			
			m_uvs[ 2*index ] = q.u[v];
			m_uvs[ 2*index+1 ] = q.v[v];
			
			m_verts[ 3*index ] = q.x[v];
			m_verts[ 3*index+1 ] = q.y[v];
			m_verts[ 3*index+2 ] = q.z[v];
			
			++index;
		}
	}
	
	assert( index == numFaces*4 );
}


